using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Timers;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace ShipSinkerNs
{
    enum GameState
    {
        Init = 0,
        PlacePieces,
        Connect,
        Connecting,
        WaitingForPlayerToConnect,
        PlayingComputer,
        DonePlacingPieces,
        MyTurn,
        OtherPlayerTurnPause,
        OtherPlayerTurn,
        MainMenu,
        ConnectingTypingIPAddress,
        Exiting,
        Winner,
        Loser
    }

    public enum GamePiece
    {
        None = 0,
        Kayak,
        Row,
        Speed,
        House,
        Barge
    }

    struct ShipStats
    {
        public Point ptGridCoords;
        public bool bRotated;
        public GamePiece gpShipType;
        public Rectangle rtInitPlace;
        public Texture2D txShip;
        public bool bSunk;
        public Shot[] segmentShots;
    }

    public enum Shot
    {
        None = 0,
        Miss,
        Hit
    }

	public enum ConnType
	{
		Receive = 1, Send
	};
	public class StateObject
	{
		public StateObject(ConnType conn) { this.connType = conn; }
		public ConnType connType = ConnType.Receive;
		public Socket workSocket = null;		// Client  socket.
		public byte[] buffer = new byte[10];	// Receive buffer.
		public MemoryStream ms = null;
	}

    public enum PacketType
    {
        Unknown = 0,
        DonePlacingShips = 1,
        Shooting = 2,
        ShotReply = 3,
        Message = 4,
        Disconnected = 5
    }

    public class Packet
    {
        public PacketType packetType;
        public Point ptShot;
        public Shot shot;
        public int bShipSunk;
        public bool bShipRotated;
        public Point ptShipGridCoords;
        public string Message;
        public GamePiece shipType;
        public bool bAllShipsSunk;
        public Packet()
        {
            packetType = PacketType.Unknown;
            shipType = GamePiece.None;
            ptShot = new Point();
            shot = Shot.None;
            bShipSunk = -1;
            bShipRotated = false;
            ptShipGridCoords = new Point();
            Message = "";
            bAllShipsSunk = false;
        }
        public byte[] GetBytes()
        {
            MemoryStream ms = new MemoryStream();
            BinaryWriter bw = new BinaryWriter(ms);
            bw.Write((int)0);
            bw.Write((int)packetType);
            switch (packetType)
            {
                case PacketType.Message:
                    bw.Write(Message);
                    break;
                case PacketType.DonePlacingShips:
                    break;
                case PacketType.Unknown:
                    break;
                case PacketType.Shooting:
                    bw.Write(ptShot.X);
                    bw.Write(ptShot.Y);
                    break;
                case PacketType.ShotReply:
                    bw.Write(ptShot.X);
                    bw.Write(ptShot.Y);
                    bw.Write((int)shot);
                    bw.Write(bShipSunk);
                    if (bShipSunk != -1)
                    {
                        bw.Write((int)shipType);
                        bw.Write(ptShipGridCoords.X);
                        bw.Write(ptShipGridCoords.Y);
                        bw.Write(bShipRotated);
                        bw.Write(bAllShipsSunk);
                    }
                    break;
            }
            int length = (int)bw.BaseStream.Length;
            bw.BaseStream.Position = 0;
            bw.Write(length);
            return ms.ToArray();
        }
    }

    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class ShipSinker : Microsoft.Xna.Framework.Game
    {
        #region Class Variables
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Texture2D Wave = null;
        Texture2D WaveTarget = null;
        Texture2D Red = null;
        Texture2D White = null;
        Texture2D Ship2 = null, Ship3 = null, Ship4 = null, Ship5 = null;
        SpriteFont Font1;
        GameState gameState = GameState.Init;
        ShipStats[] shipStats, compShipStats;
        bool bRKeyDown = false;
        Shot[,] myShips = new Shot[10, 10];
        Shot[,] myShots = new Shot[10, 10];
        Shot[,] compShips = new Shot[10, 10];
        Shot[,] compShots = new Shot[10, 10];
        bool bLeftButtonHeld = false;
        int gamePiece = -1;
        bool bPlayComputer = false;
        System.Timers.Timer t = new System.Timers.Timer();
        KeyboardState prevKs;
        private StateObject soSend = new StateObject(ConnType.Send);
        private StateObject soRec = new StateObject(ConnType.Receive);
        public AsyncCallback pfnCallBack = null;
        Queue Q = new Queue();
        bool bConnected = false;
        bool bOtherPlayerDonePlacingShips = false;
        bool bReceiver = false; // used to tell which person "received" from the main menu; they will get the first shot
        #endregion

        public ShipSinker()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            graphics.PreferredBackBufferHeight = 600;
            graphics.PreferredBackBufferWidth = 800;
            Window.Title = "Ship Sinker";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
//            this.Window.AllowUserResizing = true;
            Init();
            pfnCallBack = new AsyncCallback(OnDataReceived);

            base.Initialize();
        }

        private void Init()
        {
            t.Elapsed += new ElapsedEventHandler(OnTimedEvent);
            t.Enabled = false;
            IsMouseVisible = true;
            shipStats = new ShipStats[5];
            compShipStats = new ShipStats[5];
            int[] shipWidths = new int[5] { 64, 96, 128, 128, 160 };

            #region Initialize the ships
            for (int i = 0; i < shipStats.Length; i++)
            {
                shipStats[i].ptGridCoords = new Point(-1, -1);
                shipStats[i].bRotated = false;
                shipStats[i].gpShipType = (GamePiece)i + 1;
                shipStats[i].rtInitPlace = new Rectangle(32, (i + 1) * 32, shipWidths[i], 32);
                shipStats[i].bSunk = false;
                shipStats[i].segmentShots = new Shot[shipStats[i].rtInitPlace.Width / 32];
                for (int segment = 0; segment < shipStats[i].segmentShots.Length; segment++)
                    shipStats[i].segmentShots[segment] = Shot.None;

                compShipStats[i].ptGridCoords = new Point(-1, -1);
                compShipStats[i].bRotated = false;
                compShipStats[i].gpShipType = (GamePiece)i + 1;
                compShipStats[i].rtInitPlace = new Rectangle(32, (i + 1) * 32, shipWidths[i], 32);
                compShipStats[i].bSunk = false;
                compShipStats[i].segmentShots = new Shot[compShipStats[i].rtInitPlace.Width / 32];
                for (int segment = 0; segment < compShipStats[i].segmentShots.Length; segment++)
                    compShipStats[i].segmentShots[segment] = Shot.None;
            }

            for (int x = 0; x < myShips.GetLength(0); x++)
                for (int y = 0; y < myShips.GetLength(1); y++)
                    myShips[x, y] = myShots[x, y] = compShips[x, y] = compShots[x, y] = Shot.None;
            InitTextures();
            #endregion
        }

        private void InitTextures()
        {
            compShipStats[0].txShip = shipStats[0].txShip = Ship2;
            compShipStats[1].txShip = shipStats[1].txShip = Ship3;
            compShipStats[2].txShip = compShipStats[3].txShip = shipStats[2].txShip = shipStats[3].txShip =Ship4;
            compShipStats[4].txShip = shipStats[4].txShip = Ship5;
        }

        /// <summary>LoadContent will be called once per game and is the place to load all of your content.</summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            Wave = Content.Load<Texture2D>("Wave");
            WaveTarget = Content.Load<Texture2D>("WaveTarget");
            Red = Content.Load<Texture2D>("RedCircle");
            White = Content.Load<Texture2D>("WhiteCircle");
            Font1 = Content.Load<SpriteFont>("SpriteFont1");
            Ship2 = Content.Load<Texture2D>("Ship2");
            Ship3 = Content.Load<Texture2D>("Ship3");
            Ship4 = Content.Load<Texture2D>("Ship4");
            Ship5 = Content.Load<Texture2D>("Ship5");
            InitTextures();
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload all content.
        /// </summary>
        protected override void UnloadContent()
        {
            Ship2.Dispose();
            Ship3.Dispose();
            Ship4.Dispose();
            Ship5.Dispose();
            Wave.Dispose();
            WaveTarget.Dispose();
            Red.Dispose();
            White.Dispose();
        }

        /// <summary>Allows the game to run logic such as updating the world, checking for collisions, gathering input, and playing audio.</summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();
            if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Escape))
                this.Exit();

            switch (gameState)
            {
                case GameState.Exiting:
                    this.Exit();
                    break;
                case GameState.MainMenu:
                    UpdateMainMenu(gameTime);
                    break;
                case GameState.Init:
                    gameState = GameState.MainMenu;
                    break;
                case GameState.PlacePieces:
                    UpdatePlacePieces(gameTime);
                    break;
                case GameState.DonePlacingPieces:
                    UpdateDonePlacingPieces(gameTime);
                    break;
                case GameState.MyTurn:
                    UpdateMyTurn(gameTime);
                    break;
                case GameState.OtherPlayerTurn:
                case GameState.OtherPlayerTurnPause:
                    UpdateOtherPlayerTurn(gameTime);
                    break;
                case GameState.Connecting:
                    UpdateConnecting(gameTime);
                    break;
                case GameState.ConnectingTypingIPAddress:
                    UpdateConnectingTypingIPAddress(gameTime);
                    break;
                case GameState.Connect:
                    UpdateConnecting(gameTime);
                    break;
                case GameState.WaitingForPlayerToConnect:
                    UpdateWaitingForPlayerToConnect(gameTime);
                    break;
            }
            prevKs = Keyboard.GetState();

            base.Update(gameTime);
        }

        void UpdateMainMenu(GameTime gameTime)
        {

        }

        void UpdatePlacePieces(GameTime gameTime)
        {
            MouseState ms = Mouse.GetState();
            Point ptMouse = new Point(ms.X, ms.Y);
            if (bLeftButtonHeld && ms.LeftButton == ButtonState.Released)   // Drop the ship
            {
                bLeftButtonHeld = false;
                if (gamePiece == -1)
                    return;
                Point pt = new Point((ms.X - 384)/32, (ms.Y - 32)/32);
                if((ms.X > 384 && ms.X < 704 && ms.Y > 32 && ms.Y < 352) && ValidShipPosition(shipStats, pt, myShips, gamePiece) // Make sure the ship is on the board
                && ((!shipStats[gamePiece].bRotated && (pt.X + shipStats[gamePiece].rtInitPlace.Width / 32) <= myShips.GetLength(0)) // make sure the ship isn't off to the right
                || (shipStats[gamePiece].bRotated && (pt.Y + shipStats[gamePiece].rtInitPlace.Width / 32) <= myShips.GetLength(1)))) // make sure the ship isn't off on the bottom
                {
                    shipStats[gamePiece].ptGridCoords = new Point(pt.X, pt.Y);
                }
                else
                {
                    shipStats[gamePiece].ptGridCoords.X = shipStats[gamePiece].ptGridCoords.Y = -1;
                    shipStats[gamePiece].bRotated = false;
                }
                gamePiece = -1;
            }
            if (ms.LeftButton == ButtonState.Pressed && !bLeftButtonHeld) // Check to see what they clicked on; if it's a ship then stick it to the mouse
            {
                bLeftButtonHeld = true;
                for (int i = 0; i < shipStats.Length; i++)
                {
                    if (shipStats[i].ptGridCoords.X == -1)
                    {
                        if (PointInRect(ptMouse, shipStats[i].rtInitPlace))
                            gamePiece = i;
                    }
                    else
                        if (PointInRect(ptMouse, new Rectangle(shipStats[i].ptGridCoords.X * 32 + 384, shipStats[i].ptGridCoords.Y * 32 + 32, shipStats[i].bRotated ? shipStats[i].rtInitPlace.Height : shipStats[i].rtInitPlace.Width, shipStats[i].bRotated ? shipStats[i].rtInitPlace.Width : shipStats[i].rtInitPlace.Height)))
                            gamePiece = i;
                }
            }
            if (gamePiece != -1 && Keyboard.GetState().IsKeyDown(Keys.R) && !bRKeyDown)
            {
                bRKeyDown = true;
                shipStats[gamePiece].bRotated = !shipStats[gamePiece].bRotated;
            }
            if(Keyboard.GetState().IsKeyUp(Keys.R))
                bRKeyDown = false;
        }

        private bool ValidShipPosition(ShipStats[] ships, Point ptGridLocation, Shot [,] map, int iThisShip)
        {
            for (int ship = 0; ship < ships.Length; ship++) // loop through the ships
            {
                if (ship == iThisShip) // validate the location of this ship
                {
                    if ((!ships[ship].bRotated && (ptGridLocation.X + ships[ship].rtInitPlace.Width / 32) > map.GetLength(0))
                    || (ships[ship].bRotated && (ptGridLocation.Y + ships[ship].rtInitPlace.Width / 32) > map.GetLength(1)))
                        return false;
                }
                
                if (ship == iThisShip || ships[ship].ptGridCoords.X == -1) // if it's not placed or this is the ship - don't check it
                    continue;
                for (int segment = 0; segment < shipStats[ship].rtInitPlace.Width / 32; segment++) // looking through the ship segments
                    for (int mySegment = 0; mySegment < ships[iThisShip].rtInitPlace.Width / 32; mySegment++)
                        if(ships[ship].ptGridCoords.X + (ships[ship].bRotated ? 0 : segment) == ptGridLocation.X + (ships[iThisShip].bRotated ? 0 : mySegment)
                        && ships[ship].ptGridCoords.Y + (ships[ship].bRotated ? segment : 0) == ptGridLocation.Y + (ships[iThisShip].bRotated ? mySegment : 0))
                            return false;
            }
            return true;
        }

        void UpdateDonePlacingPieces(GameTime gameTime)
        {
            #region Playing the computer
            if (bPlayComputer == true)
            {
                for (int i = 0; i < compShipStats.Length; i++)
                {
                    Random r = new Random();
                    while (true)
                    {
                        Point pt = new Point(r.Next(0, compShips.GetLength(0)), r.Next(0, compShips.GetLength(1)));
                        compShipStats[i].bRotated = Convert.ToBoolean(r.Next(0, 2));
                        if (ValidShipPosition(compShipStats, pt, compShips, i))
                        {
                            compShipStats[i].ptGridCoords = pt;
                            break;
                        }
                    }
                }
                gameState = GameState.MyTurn;
                return;
            }
            #endregion

            #region Playing another player
            if (Q.Count > 0)
            {
                Packet p = (Packet)Q.Dequeue(); ;
                if (p.packetType == PacketType.DonePlacingShips)
                    bOtherPlayerDonePlacingShips = true;
            }
            if (bOtherPlayerDonePlacingShips)
            {
                SetStatusLabel("");
                if (bReceiver)
                    gameState = GameState.MyTurn;
                else
                    gameState = GameState.OtherPlayerTurn;
            }
            #endregion
        }

        void UpdateMyTurn(GameTime gameTime)
        {
            MouseState ms = Mouse.GetState();
            if (ms.LeftButton == ButtonState.Released) // reset the left mouse button
                bLeftButtonHeld = false;
            if (ms.LeftButton == ButtonState.Pressed && bLeftButtonHeld == false) // check to see if this is the first instance of left click
            {
                bLeftButtonHeld = true; // set the left click so we don't enter here again
                if (ms.X > 32 && ms.X < 352 && ms.Y > 32 && ms.Y < 352) // they clicked on the correct grid
                {
                    Point pt = new Point((ms.X - 32) / 32, (ms.Y - 32) / 32);
                    if (myShots[pt.X, pt.Y] == 0) // haven't shot there
                    {
                        #region Playing the computer
                        if (bPlayComputer)
                        {
                            Shot shot = CheckShot(compShipStats, pt);
                            myShots[pt.X, pt.Y] = compShips[pt.X, pt.Y] = shot;
                            if (shot == Shot.Hit)
                            {
                                int iSunk = CheckShipSunk(compShipStats, pt);
                                if (AllShipsSunk(compShipStats))
                                {
                                    gameState = GameState.Winner;
                                    return;
                                }
                            }
                            gameState = GameState.OtherPlayerTurnPause;
                            t.Enabled = true;
                            t.Interval = 2000;
                            return;
                        }
                        #endregion

                        #region Playing another player
                        if (bConnected)
                        {
                            gameState = GameState.OtherPlayerTurnPause;
                            Packet p = new Packet();
                            p.packetType = PacketType.Shooting;
                            p.ptShot.X = pt.X;
                            p.ptShot.Y = pt.Y;
                            soSend.workSocket.Send(p.GetBytes());
                        }
                        #endregion
                    }
                }
            }
        }

        public void OnTimedEvent(object source, ElapsedEventArgs e)
        {
            t.Enabled = false;
            gameState = GameState.OtherPlayerTurn;
        }

        /// <summary>Checks to see if all ships are sunk</summary>
        /// <param name="ships">The array of ShipStats to check</param>
        /// <returns>True if all ships have been sunk</returns>
        bool AllShipsSunk(ShipStats[] ships)
        {
            for (int ship = 0; ship < ships.Length; ship++)
                if (!ships[ship].bSunk)
                    return false;
            return true;
        }

        /// <summary>Returns -1 on no ship sunk, # otherwise</summary>
        /// <param name="ships">The ship array to check</param>
        /// <param name="shot">Point of the x,y of the shot to check</param>
        /// <returns></returns>
        int CheckShipSunk(ShipStats[] ships, Point shot)
        {
            bool bHit = false;
            int ship = 0;
            for (; ship < ships.Length; ship++)
            {
                for (int segment = 0; segment < ships[ship].segmentShots.Length; segment++)
                {
                    if (ships[ship].ptGridCoords.X + (ships[ship].bRotated ? 0 : segment) == shot.X
                    && ships[ship].ptGridCoords.Y + (ships[ship].bRotated ? segment : 0) == shot.Y)
                    {
                        ships[ship].segmentShots[segment] = Shot.Hit;
                        bHit = true;
                        break;
                    }
                }
                if (bHit)
                    break;
            }
            if(ship >= ships.Length)
                return -1; // didn't hit a ship, return
            for (int segment = 0; segment < ships[ship].segmentShots.Length; segment++)
            {
                if (ships[ship].segmentShots[segment] != Shot.Hit)
                    return -1;
            }
            ships[ship].bSunk = true;
            return ship;
        }

        bool GetShotOnUnsunkenShip(Shot[,] shots, ShipStats[] ships, out Point pt)
        {
            // I could loop through the shots and see but I'd have to check the ships anyway with my setup
            // Easier just to loop through ships and see if any are hit but not sunk and determine a shot from there
//            bool bFound = false;
            for (int i = 0; i < ships.Length; i++)
            {
                if (ships[i].bSunk)
                    continue;
                for (int j = 0; j < ships[i].segmentShots.Length; j++)
                {
                    if (ships[i].segmentShots[j] == Shot.Hit)
                    {
                        pt.X = ships[i].ptGridCoords.X + (ships[i].bRotated ? 0 : j);
                        pt.Y = ships[i].ptGridCoords.Y + (ships[i].bRotated ? j : 0);
                        return true;
                    }
                }
            }
            pt.X = 0;
            pt.Y = 0;
            return false;
        }

        void UpdateOtherPlayerTurn(GameTime gameTime)
        {
            #region Computer's move, decide where to shoot
            if (bPlayComputer && gameState == GameState.OtherPlayerTurn)
            {
                // Better shots
                Point pt = new Point();
                bool bHasUnsunkShip = GetShotOnUnsunkenShip(compShots, shipStats, out pt);
                if (bHasUnsunkShip)
                {
                    // Figure out where to shoot according to the shot in a ship that hasn't been sunk in pt
                    Point ptAim = new Point(pt.X, pt.Y);
                    
                    bool bFound = false;
                    bool bHit = false;
                    int iDirection = 0;
                    while (!bFound)
                    {
                        bHit = false;
                        if (!bHit)
                        {
                            Random r = new Random();
                            iDirection = r.Next(0, 4);
                        }
                        switch (iDirection)
                        {
                            case 0: // Shooting above the pt
                                if (ptAim.Y == 0)
                                    continue;
                                ptAim.Y--;
                                break;
                            case 1: // Shooting Right of the pt
                                if (ptAim.X >= compShots.GetLength(0)-1)
                                    continue;
                                ptAim.X++;
                                break;
                                case 2: // Shooting Down from the pt
                                if (ptAim.Y >= compShots.GetLength(1)-1)
                                    continue;
                                ptAim.Y++;
                                break;
                            case 3: // Shooting Left of the pt
                                if (ptAim.X == 0)
                                    continue;
                                ptAim.X--;
                                break;
                        }
                        if (compShots[ptAim.X, ptAim.Y] == Shot.None)
                        {
                            pt = ptAim;
                            bFound = true;
                            break;
                        }
                        if (compShots[ptAim.X, ptAim.Y] == Shot.Miss)
                        {
                            ptAim = pt;
                            continue;
                        }
                        bHit = true;
                    }
                }
                else
                {
                    // Random shot below
                    Random r = new Random();
                    bool bValidShot = false;
                    while (!bValidShot)
                    {
                        pt.X = r.Next(0, compShots.GetLength(0));
                        pt.Y = r.Next(0, compShots.GetLength(1));
                        if (compShots[pt.X, pt.Y] == Shot.None)
                            bValidShot = true;
                    }
                }
                Shot shot = CheckShot(shipStats, pt);
                compShots[pt.X, pt.Y] = myShips[pt.X, pt.Y] = shot;
                if (shot == Shot.Hit)
                {
                    CheckShipSunk(shipStats, pt);
                    if (AllShipsSunk(shipStats))
                    {
                        gameState = GameState.Loser;
                        return;
                    }
                }
                gameState = GameState.MyTurn;
            }
            #endregion

            #region Online player's turn
            if (!bPlayComputer)
            {
                if (Q.Count > 0)
                {
                    Packet p = (Packet)Q.Dequeue();
                    if (p.packetType == PacketType.Shooting)
                    {
                        myShips[p.ptShot.X, p.ptShot.Y] = p.shot = CheckShot(shipStats, p.ptShot);
                        p.packetType = PacketType.ShotReply;
                        p.bShipSunk = CheckShipSunk(shipStats, p.ptShot);
                        if (p.bShipSunk != -1)
                        {
                            p.shipType = shipStats[p.bShipSunk].gpShipType;
                            p.ptShipGridCoords = shipStats[p.bShipSunk].ptGridCoords;
                            p.bShipRotated = shipStats[p.bShipSunk].bRotated;
                            p.bAllShipsSunk = AllShipsSunk(shipStats);
                        }
                        if (p.bAllShipsSunk)
                            gameState = GameState.Loser;
                        else
                            gameState = GameState.MyTurn;
                        if(bConnected)
                            soSend.workSocket.Send(p.GetBytes());
                        return;
                    }
                    if (p.packetType == PacketType.ShotReply)
                    {
                        myShots[p.ptShot.X, p.ptShot.Y] = p.shot;
                        if (p.bShipSunk != -1)
                        {
                            compShipStats[p.bShipSunk].bSunk = true;
                            compShipStats[p.bShipSunk].bRotated = p.bShipRotated;
                            compShipStats[p.bShipSunk].gpShipType = p.shipType;
                            compShipStats[p.bShipSunk].ptGridCoords = p.ptShipGridCoords;
                            if (p.bAllShipsSunk)
                                gameState = GameState.Winner;
                            else
                                gameState = GameState.OtherPlayerTurn;
                        }
                    }
                }
            }
            #endregion
        }

        /// <summary>Checks to see if a point hits any of the ships</summary>
        /// <param name="ships">An array of ShipStats to check</param>
        /// <param name="shot">The point to check in the array of ships</param>
        /// <returns><code>Shot</code></returns>
        Shot CheckShot(ShipStats[] ships, Point shot) // 1=miss, 2=hit
        {
            for (int i = 0; i < ships.Length; i++) // loop through the ships
                for (int segment = 0; segment < ships[i].rtInitPlace.Width / 32; segment++)
                    if (ships[i].ptGridCoords.X + (ships[i].bRotated ? 0 : segment) == shot.X
                    && ships[i].ptGridCoords.Y + (ships[i].bRotated ? segment : 0) == shot.Y)
                        return Shot.Hit;
            return Shot.Miss;
        }

        void UpdateConnecting(GameTime gameTime)
        {

        }
        string strIPAddress = "127.0.0.1";
        void UpdateConnectingTypingIPAddress(GameTime gameTime)
        {
            KeyboardState ks = Keyboard.GetState();
            if (prevKs.IsKeyDown(Keys.Enter) && ks.IsKeyUp(Keys.Enter))
            {
                ConnectSend();
                gameState = GameState.Connect;
                return;
            }
            if (prevKs.IsKeyDown(Keys.Delete) && ks.IsKeyUp(Keys.Delete) || (prevKs.IsKeyDown(Keys.Back) && ks.IsKeyUp(Keys.Back)))
                if(strIPAddress.Length > 0)
                    strIPAddress = strIPAddress.Substring(0, strIPAddress.Length - 1);
            if (strIPAddress.Length >= 15)
                return;
            if (prevKs.IsKeyDown(Keys.NumPad0) && ks.IsKeyUp(Keys.NumPad0) || prevKs.IsKeyDown(Keys.D0) && ks.IsKeyUp(Keys.D0))
                strIPAddress += "0";
            if (prevKs.IsKeyDown(Keys.NumPad1) && ks.IsKeyUp(Keys.NumPad1) || prevKs.IsKeyDown(Keys.D1) && ks.IsKeyUp(Keys.D1))
                strIPAddress += "1";
            if (prevKs.IsKeyDown(Keys.NumPad2) && ks.IsKeyUp(Keys.NumPad2) || prevKs.IsKeyDown(Keys.D2) && ks.IsKeyUp(Keys.D2))
                strIPAddress += "2";
            if (prevKs.IsKeyDown(Keys.NumPad3) && ks.IsKeyUp(Keys.NumPad3) || prevKs.IsKeyDown(Keys.D3) && ks.IsKeyUp(Keys.D3))
                strIPAddress += "3";
            if (prevKs.IsKeyDown(Keys.NumPad4) && ks.IsKeyUp(Keys.NumPad4) || prevKs.IsKeyDown(Keys.D4) && ks.IsKeyUp(Keys.D4))
                strIPAddress += "4";
            if (prevKs.IsKeyDown(Keys.NumPad5) && ks.IsKeyUp(Keys.NumPad5) || prevKs.IsKeyDown(Keys.D5) && ks.IsKeyUp(Keys.D5))
                strIPAddress += "5";
            if (prevKs.IsKeyDown(Keys.NumPad6) && ks.IsKeyUp(Keys.NumPad6) || prevKs.IsKeyDown(Keys.D6) && ks.IsKeyUp(Keys.D6))
                strIPAddress += "6";
            if (prevKs.IsKeyDown(Keys.NumPad7) && ks.IsKeyUp(Keys.NumPad7) || prevKs.IsKeyDown(Keys.D7) && ks.IsKeyUp(Keys.D7))
                strIPAddress += "7";
            if (prevKs.IsKeyDown(Keys.NumPad8) && ks.IsKeyUp(Keys.NumPad8) || prevKs.IsKeyDown(Keys.D8) && ks.IsKeyUp(Keys.D8))
                strIPAddress += "8";
            if (prevKs.IsKeyDown(Keys.NumPad9) && ks.IsKeyUp(Keys.NumPad9) || prevKs.IsKeyDown(Keys.D9) && ks.IsKeyUp(Keys.D9))
                strIPAddress += "9";
            if (prevKs.IsKeyDown(Keys.OemPeriod) && ks.IsKeyUp(Keys.OemPeriod) || (prevKs.IsKeyDown(Keys.Decimal) && ks.IsKeyUp(Keys.Decimal)))
                strIPAddress += ".";
        }
        void UpdateWaitingForPlayerToConnect(GameTime gameTime)
        {

        }

        #region Drawing States
        /// <summary>This is called when the game should draw itself.</summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);
            switch(gameState)
            {
                case GameState.MainMenu:
                    DrawMainMenu(gameTime);
                    break; 
                case GameState.PlacePieces:
                case GameState.DonePlacingPieces:
                    DrawPlacingPieces(gameTime);
                    break;
                case GameState.MyTurn:
                case GameState.Loser:
                case GameState.Winner:
                case GameState.OtherPlayerTurn:
                case GameState.OtherPlayerTurnPause:
                    DrawGame(gameTime);
                    break;
                case GameState.ConnectingTypingIPAddress:
                case GameState.Connecting:
                case GameState.Connect:
                case GameState.WaitingForPlayerToConnect:
                    DrawConnecting(gameTime);
                    break;
            }
            spriteBatch.End();

            base.Draw(gameTime);
        }

        void DrawGame(GameTime gameTime)
        {
            Vector2 v = new Vector2();
            Vector2 v0 = new Vector2(0, 0);

            #region Draw the water for your ships and shots, also draw hits and misses
            for (int x = 0; x < myShips.GetLength(0); x++)
            {
                for(int y = 0; y < myShips.GetLength(1); y++)
                {
                    v.X = 32 + x*32;
                    v.Y = 32 + y*32;
                    spriteBatch.Draw(Wave, v, null, Color.White, 0.0f, v0, 1, SpriteEffects.None, 1);
                    if (myShots[x, y] == Shot.Miss) // miss
                        spriteBatch.Draw(White, v, null, Color.White, 0.0f, v0, 1, SpriteEffects.None, 0);
                    if (myShots[x, y] == Shot.Hit) // hit
                        spriteBatch.Draw(White, v, null, Color.Red, 0.0f, v0, 1, SpriteEffects.None, 0);

                    v.X = 384 + x * 32;
                    spriteBatch.Draw(Wave, v, null, Color.White, 0.0f, v0, 1, SpriteEffects.None, 1);
                    if (myShips[x, y] == Shot.Miss)
                        spriteBatch.Draw(White, v, null, Color.White, 0.0f, v0, 1, SpriteEffects.None, 0);
                    if (myShips[x, y] == Shot.Hit)
                        spriteBatch.Draw(White, v, null, Color.Red, 0.0f, v0, 1, SpriteEffects.None, 0);
                }
            }
            #endregion

            #region Draw your ships
            foreach (ShipStats ss in shipStats)
            {
                v.X = ss.ptGridCoords.X * 32 + 384 + (ss.bRotated ? 32 : 0);
                v.Y = ss.ptGridCoords.Y * 32 + 32;
                spriteBatch.Draw(ss.txShip, v, null, Color.White, ss.bRotated ? (float)Math.PI / 2 : 0.0f, v0, 1, SpriteEffects.None, 0.5f);
            }
            #endregion

            #region Draw the other player ships you have sunk
            foreach (ShipStats ss in compShipStats)
            {
                if (ss.bSunk)
                {
                    v.X = ss.ptGridCoords.X * 32 + 32 + (ss.bRotated ? 32 : 0);
                    v.Y = ss.ptGridCoords.Y * 32 + 32;
                    spriteBatch.Draw(ss.txShip, v, null, Color.White, ss.bRotated ? (float)Math.PI / 2 : 0.0f, v0, 1, SpriteEffects.None, 0.5f);
                }
            }
            #endregion

            #region Draw the text for whose turn it is
            string output = "";
            if (gameState == GameState.Winner)
                output = "You win!";
            if (gameState == GameState.Loser)
                output = "You lose.";
            if (gameState == GameState.MyTurn)
                output = "Choose your target.";
            if (gameState == GameState.OtherPlayerTurn || gameState == GameState.OtherPlayerTurnPause)
                output = "Waiting on the other player";
            Vector2 FontOrigin = Font1.MeasureString(output) / 2;
            if (output.Length != 0)
            {
                v.X = 32;
                v.Y = 368;
                spriteBatch.DrawString(Font1, output, v, Color.White, 0, v0, 1.0f, SpriteEffects.None, 0.5f);
            }
            string status = GetStatusLabel();
            if (status.Length != 0)
            {
                FontOrigin = Font1.MeasureString(status) / 2;
                v.X = 32;
                v.Y = 550;
                spriteBatch.DrawString(Font1, status, v, Color.White, 0, v0, 1.0f, SpriteEffects.None, 0.5f);
            }
            #endregion

            #region Draw the word "Menu"
            output = "Menu";
            v.X = 750;
            v.Y = 570;
            FontOrigin = Font1.MeasureString(output) / 2;
            MouseState ms = Mouse.GetState();
            Point pt = new Point(ms.X, ms.Y);
            if (PointInRect(pt, new Rectangle(Convert.ToInt32(v.X - FontOrigin.X), Convert.ToInt32(v.Y - FontOrigin.Y), Convert.ToInt32(Font1.MeasureString(output).X), Convert.ToInt32(Font1.MeasureString(output).Y))))
            {
                if (ms.LeftButton == ButtonState.Pressed)
                {
                    Init();
                    gameState = GameState.MainMenu;
                    return;
                }
                spriteBatch.DrawString(Font1, output, v, Color.Violet, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            }
            else
                spriteBatch.DrawString(Font1, output, v, Color.White, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            #endregion
        }

        void DrawConnecting(GameTime gameTime)
        {
            #region Draw the title - Ship Sinker
            MouseState ms = Mouse.GetState();
            Point ptMouse = new Point(ms.X, ms.Y);
            Vector2 FontPos = new Vector2(Window.ClientBounds.Width / 2, 100);
            string output = "Ship Sinker";
            Vector2 FontOrigin = Font1.MeasureString(output) / 2;
            spriteBatch.DrawString(Font1, output, FontPos, Color.Red, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            #endregion

            #region Draw "Connect to player"
            output = "Connect to Player: " + strIPAddress;
            FontPos.Y = 150;
            FontOrigin = Font1.MeasureString(output) / 2;
            spriteBatch.DrawString(Font1, output, FontPos, Color.White, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            #endregion

            #region Draw "Connecting..."
            if (gameState == GameState.Connecting || gameState == GameState.Connect)
            {
                output = "Connecting...";
                FontPos.Y = 180;
                FontOrigin = Font1.MeasureString(output) / 2;
                spriteBatch.DrawString(Font1, output, FontPos, Color.White, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            }
            FontPos.Y = 210;
            FontOrigin = Font1.MeasureString(strStatus) / 2;
            spriteBatch.DrawString(Font1, strStatus, FontPos, Color.White, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            #endregion
        }

        void DrawPlacingPieces(GameTime gameTime)
        {
            Vector2 v = new Vector2(), v0 = new Vector2(0.0f, 0.0f);
            #region Draw the board to place your ships
            for (int x = 0; x < 10; x++)
            {
                for (int y = 0; y < 10; y++)
                {
                    v.X = 384 + x * 32;
                    v.Y = 32 + y * 32;
                    spriteBatch.Draw(Wave, v, null, Color.White, 0.0f, v0, 1, SpriteEffects.None, 1);
                }
            }
            #endregion

            #region Draw your ships
            MouseState ms = Mouse.GetState();
            foreach (ShipStats ss in shipStats)
            {
                if (gamePiece != -1 && ss.gpShipType == shipStats[gamePiece].gpShipType) // we're moving this piece, put it on the mouse
                    v = new Vector2(ms.X + (ss.bRotated ? 32 : 0)-16, ms.Y-16);
                else if (ss.ptGridCoords.X == -1) // piece hasn't been placed, draw in initial place
                {
                    v.X = ss.rtInitPlace.Left;
                    v.Y = ss.rtInitPlace.Top;
                }
                else
                {
                    v.X = ss.ptGridCoords.X * 32 + 384 + (ss.bRotated ? 32 : 0);
                    v.Y = ss.ptGridCoords.Y * 32 + 32;
                }
                spriteBatch.Draw(ss.txShip, v, null, Color.White, ss.bRotated ? (float)Math.PI/2 : 0.0f, v0, 1, SpriteEffects.None, 0);
            }
            #endregion

            #region Draw the Menu text
            #region Draw the "Done" menu option
            Point ptMouse = new Point(ms.X, ms.Y);
            Vector2 FontPos = new Vector2(750, 540);
            string output = "Done";
            Vector2 FontOrigin = Font1.MeasureString(output) / 2;
            if (PointInRect(ptMouse, new Rectangle(Convert.ToInt32(FontPos.X - (FontOrigin.X)), Convert.ToInt32(FontPos.Y - (FontOrigin.Y)), Convert.ToInt32(Font1.MeasureString(output).X), Convert.ToInt32(Font1.MeasureString(output).Y))))
            {
                if (ms.LeftButton == ButtonState.Pressed)
                {
                    if (!bPlayComputer && bConnected)
                    {
                        Packet p = new Packet();
                        p.packetType = PacketType.DonePlacingShips;
                        soSend.workSocket.Send(p.GetBytes());
                        SetStatusLabel("Waiting on other player...");
                    }
                    gameState = GameState.DonePlacingPieces;
                }
                spriteBatch.DrawString(Font1, output, FontPos, Color.Violet, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            }
            else
                spriteBatch.DrawString(Font1, output, FontPos, Color.White, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            #endregion

            #region Draw the "Menu" menu option
            output = "Menu";
            FontPos.Y = 570;
            FontOrigin = Font1.MeasureString(output) / 2;
            if (PointInRect(ptMouse, new Rectangle(Convert.ToInt32(FontPos.X - (FontOrigin.X)), Convert.ToInt32(FontPos.Y - (FontOrigin.Y)), Convert.ToInt32(Font1.MeasureString(output).X), Convert.ToInt32(Font1.MeasureString(output).Y))))
            {
                if (ms.LeftButton == ButtonState.Pressed)
                    gameState = GameState.MainMenu;
                spriteBatch.DrawString(Font1, output, FontPos, Color.Violet, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            }
            else
                spriteBatch.DrawString(Font1, output, FontPos, Color.White, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            #endregion

            #region Draw the "Rotate" text
            output = "Press 'R' to Rotate";
            FontPos.X = 32;
            FontPos.Y = 336;
            FontOrigin.X = 0;
            FontOrigin.Y = 0;
            spriteBatch.DrawString(Font1, output, FontPos, Color.White, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            #endregion

            #region Draw the status text
            string status = GetStatusLabel();
            if (status.Length != 0)
            {
                FontOrigin = Font1.MeasureString(status) / 2;
                v.X = 32;
                v.Y = 550;
                spriteBatch.DrawString(Font1, status, v, Color.White, 0, v0, 1.0f, SpriteEffects.None, 0.5f);
            }
            #endregion

            #endregion
        }

        private void DrawMainMenu(GameTime gameTime)
        {
            #region Draw the title - Ship Sinker
            MouseState ms = Mouse.GetState();
            Point ptMouse = new Point(ms.X, ms.Y);
            Vector2 FontPos = new Vector2(Window.ClientBounds.Width/2, 100);
            string output = "Ship Sinker";
            Vector2 FontOrigin = Font1.MeasureString(output) / 2;
            spriteBatch.DrawString(Font1, output, FontPos, Color.Red, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            #endregion

            #region Draw the "Play Computer" menu option
            output = "Play Computer";
            FontPos.Y = 150;
            FontOrigin = Font1.MeasureString(output) / 2;
            if (PointInRect(ptMouse, new Rectangle(Convert.ToInt32(FontPos.X - (FontOrigin.X)), Convert.ToInt32(FontPos.Y - (FontOrigin.Y)), Convert.ToInt32(Font1.MeasureString(output).X), Convert.ToInt32(Font1.MeasureString(output).Y))))
            {
                if (ms.LeftButton == ButtonState.Pressed)
                {
                    gameState = GameState.PlacePieces;
                    bPlayComputer = true;
                }
                spriteBatch.DrawString(Font1, output, FontPos, Color.Violet, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            }
            else
                spriteBatch.DrawString(Font1, output, FontPos, Color.White, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            #endregion

            #region Draw the "Wait for Player" menu option
            output = "Wait for Player";
            FontPos.Y = 180;
            FontOrigin = Font1.MeasureString(output) / 2;
            if (PointInRect(ptMouse, new Rectangle(Convert.ToInt32(FontPos.X - (FontOrigin.X)), Convert.ToInt32(FontPos.Y - (FontOrigin.Y)), Convert.ToInt32(Font1.MeasureString(output).X), Convert.ToInt32(Font1.MeasureString(output).Y))))
            {
                if (ms.LeftButton == ButtonState.Pressed)
                {
                    gameState = GameState.WaitingForPlayerToConnect;
                    bReceiver = true;
                    ConnectReceive();
                }
                spriteBatch.DrawString(Font1, output, FontPos, Color.Violet, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            }
            else
                spriteBatch.DrawString(Font1, output, FontPos, Color.White, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            #endregion

            #region Draw the "Connect to Player" menu option
            output = "Connect to Player";
            FontPos.Y = 210;
            FontOrigin = Font1.MeasureString(output) / 2;
            if (PointInRect(ptMouse, new Rectangle(Convert.ToInt32(FontPos.X - (FontOrigin.X)), Convert.ToInt32(FontPos.Y - (FontOrigin.Y)), Convert.ToInt32(Font1.MeasureString(output).X), Convert.ToInt32(Font1.MeasureString(output).Y))))
            {
                if(ms.LeftButton == ButtonState.Pressed)
                    gameState = GameState.ConnectingTypingIPAddress;
                spriteBatch.DrawString(Font1, output, FontPos, Color.Violet, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            }
            else
                spriteBatch.DrawString(Font1, output, FontPos, Color.White, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            #endregion

            #region Draw the "Exit" menu option
            output = "Exit";
            FontPos.Y = 240;
            FontOrigin = Font1.MeasureString(output) / 2;
            if (PointInRect(ptMouse, new Rectangle(Convert.ToInt32(FontPos.X - (FontOrigin.X)), Convert.ToInt32(FontPos.Y - (FontOrigin.Y)), Convert.ToInt32(Font1.MeasureString(output).X), Convert.ToInt32(Font1.MeasureString(output).Y))))
            {
                if(ms.LeftButton == ButtonState.Pressed)
                    gameState = GameState.Exiting;
                spriteBatch.DrawString(Font1, output, FontPos, Color.Violet, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            }
            else
                spriteBatch.DrawString(Font1, output, FontPos, Color.White, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
            #endregion
        }
        #endregion

        /// <summary>Returns true if the point is in the rectangle</summary>
        /// <param name="pt">Point to check</param>
        /// <param name="rect">Rectangle to check</param>
        /// <returns>True if point is in the rectangle</returns>
        private bool PointInRect(Point pt, Rectangle rect)
        {
            if (pt.X > rect.Left && pt.X < rect.Right && pt.Y > rect.Top && pt.Y < rect.Bottom)
                return true;
            else
                return false;
        }

        #region Socket
        Mutex mutSetStatus = new Mutex(false, "SetStatus");
        string strStatus = "";
        public void SetStatusLabel(string strMessage)
        {
            mutSetStatus.WaitOne();
            strStatus = strMessage;
            mutSetStatus.ReleaseMutex();
        }
        public string GetStatusLabel()
        {
            mutSetStatus.WaitOne();
            string status = strStatus;
            mutSetStatus.ReleaseMutex();
            return status;
        }

        #region Send
        private void ConnectSend()
        {
            try
            {
                SetStatusLabel("Attempting connection...");
                soSend.workSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPHostEntry ipHostEntry = Dns.GetHostEntry(strIPAddress);
                if (ipHostEntry.AddressList.Length == 0)
                {
                    SetStatusLabel("IP Address didn't resolve");
                    return;
                }
                IPAddress ip = null;
                foreach (IPAddress ipTest in ipHostEntry.AddressList)
                {
                    if (ipTest.IsIPv6LinkLocal
                    || ipTest.IsIPv6Multicast
                    || ipTest.IsIPv6SiteLocal)
                        continue;
                    ip = ipTest;
                    break;
                }
                if (ip == null)
                {
                    SetStatusLabel("IP address invalid");
                    return;
                }
                IPEndPoint ipAddress = new IPEndPoint(ip, Convert.ToInt32(8000));
                soSend.workSocket.BeginConnect(ipAddress, new AsyncCallback(CompleteConnectionCallback), soSend);
                SetStatusLabel("Connecting...");
            }
            catch (SocketException ex)
            {
                if (ex.ErrorCode == 11001)
                {
                    SetStatusLabel("Unknown host, not connected");
                    soSend.workSocket = null;
                    return;
                }
                SetStatusLabel("SOCKETEXCEPTION(ConnectSend): " + ex.Message);
                soSend.workSocket = null;
                return;
            }
            catch (Exception ex)
            {
                SetStatusLabel("EXCEPTION(ConnectSend): " + ex.Message);
                soSend.workSocket = null;
                return;
            }
        }
        #endregion

        #region Receive
        private void ConnectReceive()
        {
            try
            {
                SetStatusLabel("Listening...");
                soRec.workSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, Convert.ToInt32(8000));
                soRec.workSocket.Bind(ipLocal);
                soRec.workSocket.Listen(4);
                soRec.workSocket.BeginAccept(new AsyncCallback(CompleteConnectionCallback), soRec);
            }
            catch (SocketException ex)
            {
                if (ex.ErrorCode == 10048)
                    SetStatusLabel("Server already running on this server/port");
                else
                    SetStatusLabel("SOCKETEXCEPTION(ConnectReceive): " + ex.Message);
                soRec.workSocket = null;
                return;
            }
            catch (Exception ex)
            {
                SetStatusLabel("EXCEPTION(ConnectReceive): " + ex.Message);
                soRec.workSocket = null;
                return;
            }
        }
        #endregion

        #region Send & Receive
        private void CompleteConnectionCallback(IAsyncResult ar)
        {
            try
            {
                StateObject so = (StateObject)ar.AsyncState;
                if ((so.connType == ConnType.Send && soSend.workSocket == null)
                || (so.connType == ConnType.Receive && soRec.workSocket == null))
                    return;
                if (so.connType == ConnType.Send)
                    soSend.workSocket.EndConnect(ar);
                else
                    soSend.workSocket = soRec.workSocket.EndAccept(ar);
                WaitForData();
                SetStatusLabel("Connected");
                bConnected = true;
                gameState = GameState.PlacePieces;
            }
            catch (SocketException ex)
            {
                if (ex.ErrorCode == 10061)
                {
                    SetStatusLabel("Unable to connect to a server");
                    soSend.workSocket = null;
                    return;
                }
                SetStatusLabel("SOCKETEXCEPTION(CompleteConnectionCallback): " + ex.Message);
                soSend.workSocket = null;
                return;
            }
            catch (Exception ex)
            {
                SetStatusLabel("EXCEPTION(CompleteConnectionCallback): " + ex.Message);
            }
        }
        private void WaitForData()
        {
            try
            {
                soSend.workSocket.BeginReceive(soSend.buffer, 0, soSend.buffer.Length, 0, pfnCallBack, soSend);
            }
            catch (Exception ex)
            {
                SetStatusLabel("Exception in WaitForData: " + ex.Message);
                return;
            }
        }
        public void OnDataReceived(IAsyncResult asyn)
        {
            try
            {
                StateObject socketData = (StateObject)asyn.AsyncState;
                if (socketData.workSocket == null)
                    return;
                int iBytesRead = socketData.workSocket.EndReceive(asyn);
                // added to fix an issue where we receive more data than our buffer can hold
                if (iBytesRead > 0)
                {
                    if (socketData.ms == null)
                        socketData.ms = new MemoryStream((byte[])socketData.buffer.Clone(), 0, iBytesRead);
                    else
                    {
                        MemoryStream ms = new MemoryStream(iBytesRead + Convert.ToInt32(socketData.ms.Length));
                        ms.Write(socketData.ms.ToArray(), 0, Convert.ToInt32(socketData.ms.Length));
                        ms.Write((byte[])socketData.buffer.Clone(), 0, iBytesRead);
                        socketData.ms = ms;
                    }
                }
                if (socketData.workSocket.Available > 0)
                {
                    socketData.workSocket.BeginReceive(socketData.buffer, 0, socketData.buffer.Length, 0, pfnCallBack, socketData);
                    return;
                }
                else
                {
                    DataReceived(socketData.ms.ToArray());
                    socketData.ms = null;
                }
                // end
                WaitForData();
            }
            catch (SocketException ex)
            {
                if (ex.ErrorCode == 10054)
                {
                    if(soRec.workSocket != null)
                        soRec.workSocket.Close();
                    if(soSend.workSocket != null)
                        soSend.workSocket.Close();
                    soSend.workSocket = soRec.workSocket = null;
                    SetStatusLabel("Client disconnected!");
                    bConnected = false;
                }
                else
                    SetStatusLabel("SOCKETEXCEPTION(OnDataReceived): " + ex.Message);
            }
            catch (Exception ex)
            {
                SetStatusLabel("EXCEPTION(OnDataReceived): " + ex.Message);
                return;
            }
        }
        void DataReceived(byte[] bytes)
        {
            Packet p = new Packet();
            MemoryStream ms = new MemoryStream(bytes);
            BinaryReader bs = new BinaryReader(ms);
            bs.ReadInt32();
            p.packetType = (PacketType)bs.ReadInt32();
            switch (p.packetType)
            {
                case PacketType.Unknown:
                    break;
                case PacketType.DonePlacingShips:
                    Q.Enqueue(p);
                    break;
                case PacketType.Shooting:
                    p.ptShot.X = (int)bs.ReadInt32();
                    p.ptShot.Y = (int)bs.ReadInt32();
                    Q.Enqueue(p);
                    break;
                case PacketType.ShotReply:
                    p.ptShot.X = bs.ReadInt32();
                    p.ptShot.Y = bs.ReadInt32();
                    p.shot = (Shot)bs.ReadInt32();
                    p.bShipSunk = bs.ReadInt32();
                    if (p.bShipSunk != -1)
                    {
                        p.shipType = (GamePiece)bs.ReadInt32();
                        p.ptShipGridCoords.X = bs.ReadInt32();
                        p.ptShipGridCoords.Y = bs.ReadInt32();
                        p.bShipRotated = bs.ReadBoolean();
                        p.bAllShipsSunk = bs.ReadBoolean();
                    }
                    Q.Enqueue(p);
                    break;
                case PacketType.Message:
                    p.Message = bs.ReadString();
                    Q.Enqueue(p);
                    break;
            }
            bs.Close();
            bs.Dispose();
            ms.Close();
            bs.Dispose();
        }
        #endregion
        #endregion
    }
}
